package com.codemonkey.lq.qd.extend.impl;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.parsing.XNode;
import org.apache.ibatis.parsing.XPathParser;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.slf4j.LoggerFactory;

import com.codemonkey.lq.qd.builder.xml.XMLSConfigBuilder;
import com.codemonkey.lq.qd.builder.xml.XMLServiceBuilder;
import com.codemonkey.lq.qd.facotry.config.SConfiguration;

/**
 * 实现service文件的增量部署,
 * 
 * 增量部署,也就是进行的添加,修改可以正常展示,但是你要是删除某个来看效果..目前无法达到!!
 * 
 * Mybatis同理
 * 
 * @author LQ
 *
 */
public class SdAutoRefresher implements Runnable {

	private final SConfiguration configuration;
	private final List<FileBean> xmlFiles = new ArrayList<FileBean>();

	public SdAutoRefresher(SConfiguration configuration) {
		super();
		this.configuration = configuration;

		getLoadedFile();
	}

	private void getLoadedFile() {
		MetaObject metaSConfig = SystemMetaObject.forObject(configuration);
		Set<String> loadedServiceResources = (Set<String>) metaSConfig
				.getValue("loadedServiceResources");
		for (String loadedServiceResource : loadedServiceResources) {
			FileBean fileBean = new FileBean();

			try {
				File file = Resources.getResourceAsFile(loadedServiceResource);
				fileBean.setFile(file);
			} catch (IOException e) {
				e.printStackTrace();
			}
			fileBean.setBeforeTime();
			this.xmlFiles.add(fileBean);
		}
	}

	@Override
	public void run() {
		try {
			refresh();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public synchronized void refresh() throws Exception {
		while (true) {
			List<FileBean> fileList = this.getModifiedFile();
			if (fileList != null) {
				System.out
						.println(">>>>>>> there are some 'service xml files' changed.so we need refresh it ... ");

				clearSpecialFieldsInConfigurationClass();

				for (FileBean fileBean : fileList) {
					doParseServiceElement(configuration, fileBean.getFile());
					fileBean.setBeforeTime();
				}

				System.out.println(">>>>>>> refresh finished  successfully ... ");
			}

			Thread.sleep(5 * 1000);

		}
	}

	private List<FileBean> getModifiedFile() throws IOException {
		List<FileBean> fileList = new ArrayList<FileBean>();
		for (FileBean fileBean : this.xmlFiles) {
			if (fileBean.isModify()) {
				fileList.add(fileBean);

			}
		}

		if (fileList != null && fileList.size() > 0) {
			return fileList;
		}
		return null;
	}

	/**
	 * 清理原有资源，更新为自己的StrictMap方便，增量重新加载
	 */
	private void clearSpecialFieldsInConfigurationClass() {
		String[] mapFieldNames = new String[] { "serviceContainer" };
		for (String fieldName : mapFieldNames) {
			MetaObject metaSConfig = SystemMetaObject.forObject(configuration);
			Map map = (Map) metaSConfig.getValue(fieldName);

			if (null == map || map instanceof com.codemonkey.lq.qd.extend.impl.StrictMap<?>) {
				continue;
			}

			Map newMap = new StrictMap(fieldName.toUpperCase() + "collection");
			for (Object key : map.keySet()) {
				try {
					newMap.put(key, map.get(key));
				} catch (IllegalArgumentException ex) {
					newMap.put(key, ex.getMessage());
				}
			}

			metaSConfig.setValue(fieldName, newMap);

		}
	}

	private void doParseServiceElement(SConfiguration configuration, File resourceFile)
			throws Exception {

		final String resourcePath = resourceFile.getAbsolutePath();
		//因为在配置文件中使用文件路径和我们上面获得的路径resourcePath不相同,所以省去了判断
		//if (!configuration.isResourceLoaded(resourcePath)) {
		//InputStream inputStream = Resources.getResourceAsStream(resourcePath);
		servicesElement(new XPathParser(new FileInputStream(resourceFile))
				.evalNode("/service-list"));
		configuration.addLoadedResource(resourcePath);

		LoggerFactory.getLogger(XMLSConfigBuilder.class).debug("service file {} has parsed",
				resourcePath);
		//}
	}

	private void servicesElement(XNode context) {
		try {
			//buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
			buildServiceFromContext(context.evalNodes("service"));
		} catch (Exception e) {
			throw new RuntimeException("Error parsing Mapper XML. Cause: " + e, e);
		}
	}

	private void buildServiceFromContext(List<XNode> list) {
		for (XNode context : list) {
			final XMLServiceBuilder builder = new XMLServiceBuilder(context, configuration, null);
			builder.parse();
		}
	}
}
